﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace GraphicSplit101
{
    /// <summary>
    /// 切割
    /// </summary>
    public partial class UCGraphicSplit : UserControl
    {
        private List<List<Rectangle>> listSplitGrayBlockPostion = null;
        private Bitmap bitmap = null;
        private int intSplitCount = 13;        //样本学习切13次，产品切割次数从外面传
        private float XRadio;
        private float YRadio;
        private Rectangle rec;

        public List<List<Rectangle>> ListSplitGrayBlockPostion
        {
            get { return listSplitGrayBlockPostion; }
        }
        public Bitmap Bitmap
        {            
            set 
            { 
                bitmap = value;
                if (bitmap != null)
                {
                    int imageWidth;
                    int imageHeight;
                    imageHeight = bitmap.Height;
                    imageWidth = bitmap.Width;
                    byte[,] grayBytes = new byte[imageWidth, imageHeight];
                    listSplitGrayBlockPostion = SplitGraphicBytes(grayBytes, intSplitCount, imageWidth, imageHeight);
                }
                else
                {
                    //错误代码：
                    string strErrMsg = "错误信息：找不到任何图像对象！请在切割操作前请先载入图像。";
                    MessageBox.Show(strErrMsg);
                }             
            }
        }
        private byte[,] availableBytes;

        public byte[,] AvailableBytes
        {
            get { return availableBytes; }
            set { availableBytes = value; }
        }


        /// <summary>
        /// 切割并返回区域值
        /// </summary>
        public List<List<Rectangle>> SplitGraphicBytes()
        {
            if (bitmap != null)
            {
                int imageWidth;
                int imageHeight;
                imageHeight = bitmap.Height;
                imageWidth = bitmap.Width;
                byte[,] grayBytes = new byte[imageWidth, imageHeight];
                listSplitGrayBlockPostion = SplitGraphicBytes(grayBytes, intSplitCount, imageWidth, imageHeight);
            }
            else if (availableBytes != null)
            {
                listSplitGrayBlockPostion = SplitGraphicBytes(availableBytes, intSplitCount, availableBytes.GetLength(0), availableBytes.GetLength(1));
            }
            return listSplitGrayBlockPostion;
        }

        public int SplitCount
        {
            set { intSplitCount = value; }
        }
        public UCGraphicSplit()
        {
            InitializeComponent();
 
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="grayBytes">图像灰度值的二维数组</param>
        /// <param name="splitCount">切割层数</param>
        /// <param name="imageWidth"></param>
        /// <param name="imageHeight"></param>        
        /// <param name="multiListBlockPosition">下标0代表left，1代表top，2代表切割块的顺序号</param>
        /// <returns></returns>
        private static List<List<Rectangle>> SplitGraphicBytes(byte[,] grayBytes, int splitCount, int imageWidth, int imageHeight)
        {
            const int intSplitBlockCount = 2;
            List<List<byte[,]>> graphicBytesList = new List<List<byte[,]>>();
            int intParentAbsX = 0;
            int intParentAbsY = 0;
            List<List<int[]>> multiListBlockPosition = new List<List<int[]>>();
            List<List<Rectangle>> multiListBlockRectangle = new List<List<Rectangle>>();
            for (int i = 0; i < splitCount; i++)
            {
                List<int[]> listBlockPosition = new List<int[]>();
                List<byte[,]> bytesList = new List<byte[,]>();

                if (i == 0)
                {
                    bytesList.AddRange((CrossSplitGraphicBytes(grayBytes, i + 1, out listBlockPosition, 0, 0, imageWidth, imageHeight)));
                }
                else
                {
                    for (int j = 0; j < Math.Pow(intSplitBlockCount, i); j++)
                    {
                        List<int[]> listBlockPositionTemp = new List<int[]>();
                        intParentAbsX = multiListBlockPosition[i - 1][j][0];
                        intParentAbsY = multiListBlockPosition[i - 1][j][1];
                        bytesList.AddRange((CrossSplitGraphicBytes(graphicBytesList[i - 1][j], i + 1, out listBlockPositionTemp, intParentAbsX, intParentAbsY, imageWidth, imageHeight)));
                        listBlockPosition.AddRange(listBlockPositionTemp);
                    }
                }
                multiListBlockPosition.Add(listBlockPosition);
                graphicBytesList.Add(bytesList);
            }
            //将int[] 转换成Rectangle
            for (int j = 0; j < splitCount; j++)
            { 
                int intBlockCount=multiListBlockPosition[j].Count;
                List<Rectangle> listRectangleNew = new List<Rectangle>();
                for(int k=0;k<intBlockCount;k++)
                {
                    //按顺序排序
                    List<int[]> listRectangle=multiListBlockPosition[j];
                    //算法中的切割次序
                    int intSeriaNO=0;
                    for(int l=0;l<intBlockCount;l++)
                    {
                        if(listRectangle[l][2]==k)
                        {
                            intSeriaNO=l;
                            break;
                        }
                    }
                    int[] intRectangle = listRectangle[intSeriaNO];
                    Rectangle rectangle = new Rectangle(intRectangle[0], intRectangle[1], intRectangle[3], intRectangle[4]);
                    listRectangleNew.Add(rectangle);        
                }
                multiListBlockRectangle.Add(listRectangleNew);
            }
            return multiListBlockRectangle;
        }
        private static List<byte[,]> CrossSplitGraphicBytes(byte[,] grayGraphic, int currentLayer, out List<int[]> listBlockPosition, int intParentAbsX, int intParentAbsY, int intImageWidth, int intImageHeight)
        {
            const int intSplitBlockCount = 2;
            //intParity: 1 奇数时横切|0 偶数时纵切
            int intParity = currentLayer % 2;
            int intWidth = grayGraphic.GetLength(0);
            int intHeight = grayGraphic.GetLength(1);
            List<byte[,]> listSplitBlock = new List<byte[,]>();
            listBlockPosition = new List<int[]>();
            if (intParity == 1)
            {
                for (int k = 0; k < intSplitBlockCount; k++)
                {                    
                    int intStartIndex = (intHeight / intSplitBlockCount * k);
                    int intMaxLength = k * intHeight / intSplitBlockCount + intHeight / intSplitBlockCount;
                    int intBlockWidth = intWidth;
                    int intBlockHeight = (intHeight + 1) / intSplitBlockCount;
                    byte[,] newByte = new byte[intBlockWidth, intBlockHeight];
                    listSplitBlock.Add(newByte);
                    //newBytePosition 0 left值 1 top值 2 实际Block序号 3 width 4 height
                    int[] newBytePosition = new int[5];
                    int intBlockSerial = 0;
                    newBytePosition[0] = intParentAbsX;
                    newBytePosition[1] = intParentAbsY + intStartIndex;
                    intBlockSerial = (newBytePosition[1] / intBlockHeight) * (intImageWidth / intBlockWidth) + newBytePosition[0] / intBlockWidth ;                    
                    newBytePosition[2] = intBlockSerial;
                    newBytePosition[3] = intBlockWidth;
                    newBytePosition[4] = intBlockHeight;
                    listBlockPosition.Add(newBytePosition);
                }
            }
            else
            {
                for (int k = 0; k < intSplitBlockCount; k++)
                {
                    int intStartIndex = (intWidth / intSplitBlockCount * k);
                    int intMaxLength = k * intWidth / intSplitBlockCount + intWidth / intSplitBlockCount;
                    int intBlockWidth = (intWidth + 1) / intSplitBlockCount;
                    int intBlockHeight = intHeight;
                    byte[,] newByte = new byte[intBlockWidth, intBlockHeight];
                    listSplitBlock.Add(newByte);
                    //newBytePosition 0 left值 1 top值 2 实际Block序号 3 width 4 height
                    int[] newBytePosition = new int[5];
                    int intBlockSerial = 0;
                    newBytePosition[0] = (intParentAbsX + intStartIndex);
                    newBytePosition[1] = intParentAbsY;
                    intBlockSerial = (newBytePosition[1] / intBlockHeight) * (intImageWidth / intBlockWidth) + newBytePosition[0] / intBlockWidth ;                    
                    newBytePosition[2] = intBlockSerial;
                    newBytePosition[3] = intBlockWidth;
                    newBytePosition[4] = intBlockHeight;
                    listBlockPosition.Add(newBytePosition);
                }
            }
            return listSplitBlock;
        }
        /// <summary>
        /// GetGrayBytes2D:下标0代表x轴坐标，1代表y轴坐标。默认提取绿色通道像素值。
        /// </summary>
        /// <param name="bitmap"></param>
        /// <returns></returns>
        private static byte[,] GetGrayBytes2D(Bitmap bitmap)
        {
            if (bitmap == null) return null;
            string strPixelFormat = bitmap.PixelFormat.ToString();
            if (strPixelFormat == "Format8bppIndexed")
            {
                return ConvertGrayBytesToDoubleArray(GetGrayBytesFor8bit(bitmap), bitmap.Width, bitmap.Height);
            }
            else if (strPixelFormat == "Format32bppArgb")
            {
                return ConvertGrayBytesToDoubleArray(GetGrayBytesFor32bit(bitmap, 2), bitmap.Width, bitmap.Height);
            }
            else
            {
                //error msg
                return null;
            }
        }
        private static byte[,] ConvertGrayBytesToDoubleArray(byte[] grayBytes, int imageWidth, int imageHeight)
        {
            byte[,] processedGrayBytes = new byte[imageWidth, imageHeight];
            int k = 0;
            for (int j = 0; j < imageHeight; j++)
            {
                for (int i = 0; i < imageWidth; i++)
                {
                    processedGrayBytes[i, j] = grayBytes[k++];
                }
            }
            return processedGrayBytes;
        }
        private static byte[] GetGrayBytesFor8bit(Bitmap bitmap)
        {
            byte[] grayValues = null;
            try
            {
                if (bitmap != null)
                {
                    Rectangle rect = new Rectangle(0, 0, bitmap.Width, bitmap.Height);
                    System.Drawing.Imaging.BitmapData bmpData = bitmap.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite, bitmap.PixelFormat);
                    IntPtr ptr = bmpData.Scan0;
                    int bytes = bitmap.Width * bitmap.Height;
                    grayValues = new byte[bytes];
                    System.Runtime.InteropServices.Marshal.Copy(ptr, grayValues, 0, bytes);
                    bitmap.UnlockBits(bmpData);
                }
                return grayValues;
            }
            catch (Exception ex)
            {
                Console.WriteLine(string.Format("GetGrayBytesFor8bit:{0}", ex.Message));
                return null;
            }
        }
        private static byte[] GetGrayBytesFor32bit(Bitmap bitmap, int intMethod)
        {
            byte[] rgbValues = null;
            byte[] grayValues = null;
            if (bitmap != null)
            {
                double r = 0, g = 0, b = 0;
                switch (intMethod)
                {
                    case 0:
                        {
                            r = 0.299;
                            g = 0.587;
                            b = 0.114;
                            break;
                        }
                    case 1:
                        {
                            r = 1;
                            g = 0;
                            b = 0;
                            break;
                        }
                    case 2:
                        {
                            r = 0;
                            g = 1;
                            b = 0;
                            break;
                        }
                    case 3:
                        {
                            r = 0;
                            g = 0;
                            b = 1;
                            break;
                        }
                    default:
                        r = 0;
                        g = 1;
                        b = 0;
                        break;
                }
                try
                {
                    Rectangle rect = new Rectangle(0, 0, bitmap.Width, bitmap.Height);
                    System.Drawing.Imaging.BitmapData bmpData = bitmap.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite, bitmap.PixelFormat);
                    IntPtr ptr = bmpData.Scan0;
                    int bytes = bitmap.Width * bitmap.Height;
                    int rgbBytes = bytes * 3;
                    rgbValues = new byte[rgbBytes];
                    grayValues = new byte[bytes];
                    System.Runtime.InteropServices.Marshal.Copy(ptr, rgbValues, 0, rgbBytes);
                    int j = 0;
                    for (int i = 0; i < rgbBytes; i += 3)
                    {
                        double colorTemp = rgbValues[i + 2] * r + rgbValues[i + 1] * g + rgbValues[i] * b;
                        grayValues[j++] = (byte)colorTemp;
                    }
                    bitmap.UnlockBits(bmpData);
                }
                catch (Exception ex)
                {
                    Console.WriteLine(string.Format("GetGrayBytesFor32bit:{0}", ex.Message));
                }
            }
            return grayValues;
        }

        private void btnSplit_Click(object sender, EventArgs e)
        {
            try
            {
                int intCurrentLayer = Convert.ToInt32(txtLayerCount.Text) - 1;
                int intRow = Convert.ToInt32(txtRow.Text);
                int intColumn = Convert.ToInt32(txtColumn.Text);
                int intBlockWidth = listSplitGrayBlockPostion[intCurrentLayer][0].Width;
                int intBlockHeight = listSplitGrayBlockPostion[intCurrentLayer][0].Height;
                int intImageWidth = PicSample.Image.Width;
                int intImageHeight = PicSample.Image.Height;
                int intBlockRowCount = intImageHeight / intBlockHeight;
                int intBlockColCount = intImageWidth / intBlockWidth;
                if (intRow > intBlockRowCount || intColumn > intBlockColCount)
                {
                    throw new Exception(string.Format("层数:{0} 的最大行数是{1} 最大列数是{2}", intCurrentLayer + 1, intBlockRowCount, intBlockColCount));
                }
                int intSerialNO = (intRow - 1) * intBlockColCount + intColumn - 1;
                rec = listSplitGrayBlockPostion[intCurrentLayer][intSerialNO];
                DrawArea(rec, PicSample);                
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }
        private void DrawArea(Rectangle rectangle, PictureBox pictureBox)
        {
            if (rectangle != null)
            {
                pictureBox.Refresh();
                Graphics g = pictureBox.CreateGraphics();
                Pen pen = new Pen(Color.Red, 2f);
                XRadio = (float)pictureBox.Width / pictureBox.Image.Width;
                YRadio = (float)pictureBox.Height / pictureBox.Image.Height;

                Rectangle rectangleTmp = new Rectangle(Convert.ToInt32(rectangle.X * XRadio), Convert.ToInt32(rectangle.Y * YRadio), Convert.ToInt32(rectangle.Width * XRadio), Convert.ToInt32(rectangle.Height * YRadio));
                g.DrawRectangle(pen, rectangleTmp);
                g.Dispose();
            }
        }
    }
}
